﻿// Inner Kit 库
// XML读取类
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 这个类用于读取XML，并将其转化为交互者友好的类。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-14

#include "xml_reader.h"
#include "tinyxml2.h"

using namespace tinyxml2;

namespace ikit {

static const XmlValue EMPTY_VALUE = {0, nullptr, 0, 0.0f};
static const XmlNode EMPTY_NODE;

////////////////////////////////////////////////////////////////////////////////
// 通用工具
////////////////////////////////////////////////////////////////////////////////
// 输入字符串，输出相应的16进制；例如：输入"12AB3d"，输出0x12AB3d
static unsigned int hex2uint(const char* buf, int prefix_len, int len) {
  int sum = 0;
  int num = 0;
  char ch = 0;
  for (int i = 0; i < len; i++) {
    // 这里的索引是从第3位开始。因为前两位是0x
    ch = buf[i + prefix_len];

    if (ch >= '0' && ch <= '9') {
      num = ch - '0';
    }

    if (ch >= 'A' && ch <= 'Z') {
      num = ch - 'A' + 10;
    }

    if (ch >= 'a' && ch <= 'z') {
      num = ch - 'a' + 10;
    }

    sum = sum * 16 + num; // 16进制表达
  }
  return sum;
}

// 这里必须严格要求类型，必须是以0x开头。
// 如果成功，返回hex部分的长度，否则返回0
static bool is0xhex(const char* s) {
  if (s == NULL) {
    return false;
  }
  int lower;

  // 必须是3位以上，至少得是0x1这样有三位数
  int len = strlen(s);
  if (len < 3) {
    return false;
  }

  // 前两位必须是0x
  if (!(s[0] == '0' && s[1] == 'x')) {
    return false;
  }

  // 这个s[i]真太妙了。原来还能这样！
  for (int i = 2; s[i]; i++) {
    lower = tolower(s[i]);

    bool isNumber = lower >= '0' && lower <= '9';
    bool isHex = lower >= 'a' && lower <= 'f';

    if (!(isNumber || isHex)) {
      return false;
    }
  }

  return true;
}

////////////////////////////////////////////////////////////////////////////////
// XML值
////////////////////////////////////////////////////////////////////////////////


void auto_set_xml_value(XmlValue& item, const char* name, const char* text) {
  // ID为Hash值
  item.id = hash64(name);
  item.value = nullptr;

  // 如果没有字符串，则不处理
  if (text == nullptr) {
    return;
  }

  // 根据值区别不同的项目
  if (str_equal(text, "true")) {
    item.int_value = 1;
    item.float_value = 1.0f;
  } else if (str_equal(text, "false")) {
    item.int_value = 0;
    item.float_value = 0.0f;
  } else if (is_number(text)) {
    item.int_value = atoi(text);
    item.float_value = static_cast<float>(item.int_value);
  } else if (is_float(text)) {
    item.float_value = atof(text);
    item.int_value = static_cast<int>(item.float_value);
  } else if (is0xhex(text) > 0) {
    item.int_value = static_cast<int>(hex_to_uint(text)); // atoi(text);
    item.float_value = static_cast<float>(item.int_value);
  } else {
    item.int_value = 0;
    item.float_value = 0.0f;
    // 值设置为字符串（需要转码）
    // value = str_converter.UTF8ToGB2312(text);
    // TODO，不知道是否应该在此处就进行转码，可能会导致非常繁重的计算。以后需要改进。
    item.value = str_copy(text);
  }
}

////////////////////////////////////////////////////////////////////////////////
// XML节点
////////////////////////////////////////////////////////////////////////////////

XmlNode::XmlNode()
    : value_{0, nullptr, 0, 0.0f}, attributes_(nullptr), attributes_count_(0), children_(nullptr),
      children_count_(0) {}

XmlNode::~XmlNode() {
  // 删除值中的字符串
  SAFE_FREE(value_.value);
  SAFE_DELETE_ARRAY(attributes_);
  SAFE_DELETE_ARRAY(children_);
}

void XmlNode::Read(XMLElement* xml_node) {
  int index = 0;
  // 得到属性
  attributes_count_ = 0;
  for (const XMLAttribute* i = xml_node->FirstAttribute(); i != nullptr; i = i->Next()) {
    attributes_count_++;
  }
  if (attributes_count_ > 0) {
    // 分配空间
    attributes_ = new XmlValue[attributes_count_];
    // 依次填充属性
    index = 0;
    for (const XMLAttribute* i = xml_node->FirstAttribute(); i != nullptr; i = i->Next()) {
      auto_set_xml_value(attributes_[index], i->Name(), i->Value());
      index++;
    }
  }

  // 得到子项目
  children_count_ = xml_node->ChildElementCount();
  if (children_count_ > 0) {
    // 分配Child空间
    children_ = new XmlNode[children_count_];
    // 将自已的值设置为空（但要设置ID）
    value_ = EMPTY_VALUE;
    value_.id = hash64(xml_node->Name());

    // 依次设置Child
    index = 0;
    for (XMLElement* child = xml_node->FirstChildElement(); child != nullptr;
         child = child->NextSiblingElement()) {
      children_[index].Read(child);
      index++;
    }
  } else {
    // 如果没有Child，则设置自已的值
    children_ = nullptr;
    auto_set_xml_value(value_, xml_node->Name(), xml_node->GetText());
  }
}

const XmlValue& XmlNode::Attr(const char* key) const {
  if (attributes_count_ == 0) {
    return EMPTY_VALUE;
  }
  std::uint64_t hashId = hash64(key);
  for (int i = 0; i < attributes_count_; i++) {
    const XmlValue& item = attributes_[i];
    if (item.id == hashId) {
      return item;
    }
  }
  return EMPTY_VALUE;
}

void XmlNode::Foreach(const char* key, XmlNodeCallback callback, void* classOwner,
                      void* itemOwner) const {
  if (children_count_ == 0) {
    return;
  }
  std::uint64_t hashId = hash64(key);
  for (int i = 0; i < children_count_; i++) {
    if (children_[i].ID() == hashId) {
      callback(children_[i], classOwner, itemOwner);
    }
  }
}

const XmlNode& XmlNode::operator[](const char* key) const {
  if (children_count_ == 0) {
    return EMPTY_NODE;
  }
  std::uint64_t hashId = hash64(key);
  for (int i = 0; i < children_count_; i++) {
    if (children_[i].ID() == hashId) {
      return children_[i];
    }
  }
  return EMPTY_NODE;
}

const XmlNode& XmlNode::Get(const char* key) const {
  if (children_count_ == 0) {
    return EMPTY_NODE;
  }
  std::uint64_t hashId = hash64(key);
  for (int i = 0; i < children_count_; i++) {
    if (children_[i].ID() == hashId) {
      return children_[i];
    }
  }
  return EMPTY_NODE;
}

////////////////////////////////////////////////////////////////////////////////
// XML读取
////////////////////////////////////////////////////////////////////////////////

XmlReader::XmlReader(const char* filename) { Load(filename); }

void XmlReader::Load(const char* filename) {
  // 创建XML文档对象
  tinyxml2::XMLDocument doc;
  tinyxml2::XMLError error = doc.LoadFile(filename);
  if (error != tinyxml2::XML_SUCCESS) {
    KLOG("Load XML failed: %s, error: %d.", filename, error);
    return;
  }

  // 获取根元素
  XMLElement* xml_root = doc.RootElement();
  if (!xml_root) {
    KLOG("Load XML failed: %s, unable find root.", filename);
    return;
  }

  root_.Read(xml_root);

  KLOG("Load XML %s successed.", filename);
}

const XmlNode& XmlReader::operator[](const char* key) const { return root_.operator[](key); }

const XmlNode& XmlReader::Get(const char* key) const { return root_.Get(key); }

} // namespace ikit